package it.polimi.jita.cp.block.dd;

import it.polimi.jita.cp.block.dd.ServiceLevel.ServiceLevelType;
import it.polimi.jita.cp.block.de.Histogram;
import it.polimi.jita.cp.block.scheduler.IVirtualMachineSchedulerData;

import java.util.Collection;
import java.util.List;

/**
 * @author Federico Monterisi
 *
 */
public abstract class AbstractVirtualMachineData implements
		IVirtualMachineSchedulerData {

	private String name;
	private Integer vCpuNum;
	private boolean likelyDemand = true;
	private List<Instant> instants;
	private Instant currentInstant;
	private ServiceLevel meanSl = new ServiceLevel();

	protected AbstractVirtualMachineData(String name, Integer vCpuNum,
			List<Instant> instants) {
		super();
		this.name = name;
		this.vCpuNum = vCpuNum;
		this.instants = instants;
	}

	public String getName() {
		return name;
	}

	public Integer getvCpuNum() {
		return vCpuNum;
	}

	@Override
	public Integer getShares() {
		return vCpuNum;
	}

	protected boolean isLikelyDemand() {
		return likelyDemand;
	}

	protected void setLikelyDemand(boolean likelyDemand) {
		this.likelyDemand = likelyDemand;
	}

	public Double getRunForVCpu() {
		return getRun() / vCpuNum;
	}

	public Double getDemandForVCpu() {
		return getDemand() / vCpuNum;
	}

	public boolean isNeedFrequency(double precision) {
		return Math.abs(getDemand() - getRun()) > precision;
	}

	@Override
	public Double getReservation() {
		return 0d;
	}

	@Override
	public Double getLimit() {
		return null;
	}

	@Override
	public Double getRun() {
		return currentInstant.getRun() * getvCpuNum();
	}

	@Override
	public void setRun(Double run) {
		currentInstant.setRun(run / getvCpuNum());

	}

	protected void setServiceLevel(ServiceLevelType slType, Double value) {
		currentInstant.setServiceLevel(slType, value);
	}

	protected List<Instant> getInstants() {
		return instants;
	}

	protected Instant getCurrentInstant() {
		return currentInstant;
	}

	protected void setCurrentInstant(Instant currentInstant) {
		this.currentInstant = currentInstant;
	}

	public Double getMeanServiceLevel(ServiceLevelType key) {
		// if (meanSl.containsSl(key))
		// return meanSl.get(key);
		meanSl.put(key, computeMeanSl(key));
		return meanSl.get(key);
	}

	private Double computeMeanSl(ServiceLevelType key) {
		return asymptoticMean(key, instants);
	}

	public static Double stdMean(ServiceLevelType key,
			Collection<Instant> instants) {
		double meanSl = 0d;
		for (Instant instant : instants) {
			meanSl += instant.getSl().get(key);
		}
		meanSl /= instants.size();
		return meanSl;
	}

	public static Double asymptoticMean(ServiceLevelType key,
			Collection<Instant> instants) {
		double meanSl = 0d;
		for (Instant instant : instants) {
			double sl = instant.getSl().get(key);
			meanSl += (2d * sl - 1) / sl;
		}
		meanSl /= instants.size();
		return meanSl;
	}

	public static Double miscMean(ServiceLevelType key,
			Collection<Instant> instants, Double criticalSl) {
		double meanSl = 0d;
		for (Instant instant : instants) {
			double sl = instant.getSl().get(key);
			if (sl < criticalSl) {
				double a = Math.pow(criticalSl, 2d);
				double c = 1 - criticalSl - a / criticalSl;
				double iSl = 1 - a / sl - c;
				meanSl += iSl;
			} else {
				meanSl += sl;
			}

		}
		meanSl /= instants.size();
		return meanSl;
	}

	public static Double cdf(ServiceLevelType slType, List<Instant> instants,
			double criticalSl) {

		// double[] slToLoad = new double[instants.size()];
		// int i = 0;
		Histogram histogram = new Histogram(100, 0, 1);
		for (Instant instant : instants) {
			// slToLoad[i++] = instant.getSl().get(slType);
			histogram.incrementBinValue(instant.getSl().get(slType));
		}
		// EmpiricalDistribution ed = new EmpiricalDistribution(100);
		// ed.load(slToLoad);
		// Double result = 1d - ed.cumulativeProbability(criticalSl);
		// if (result.equals(Double.NaN))
		// result = 1d - histogram.cdf(criticalSl);
		Double result = 1d - histogram.cdf(criticalSl);
		return result;

	}
}
